# encoding:utf-8

import json, time, pytz, requests,yaml,logging, re
from datetime import datetime, timedelta, timezone
import MetaTrader5 as mt5
import numpy as np
import pandas as pd
from sqlalchemy import create_engine
import talib as ta
import matplotlib
from datetime import datetime, timedelta
from talib import abstract
import mplfinance as mpf
from apscheduler.schedulers.background import BackgroundScheduler
import sqlite3
import talib
# pd.options.mode.copy_on_write = True

def calculate_ema(data, window):
    alpha = 2 / (window + 1)
    ema_values = np.full(data.shape, np.nan)
    for i in range(window - 1, len(data)):
        if not np.isnan(data[i]):
            ema_values[i] = _ema(data[i - window + 1:i + 1])
    return ema_values


def _ema(arr):
    alpha = 2 / (len(arr) + 1)
    prev = arr[0]
    for num in arr[1:]:
        if not np.isnan(num):
            prev = alpha * num + (1 - alpha) * prev
    return prev


def EMA(data, window):
    return pd.DataFrame(data).ewm(span=window, adjust=False).mean()


def _MACD(close, short=12, long=26, mid=9):
    # 计算短期EMA和长期EMA
    ema_short = EMA(close, short)
    ema_long = EMA(close, long)
    # 计算DIF
    dif = ema_short - ema_long
    # 计算DEA
    # w = 2.0 / (mid + 1)
    # w1 = 1-w
    dea = pd.DataFrame(dif).ewm(alpha=mid, adjust=False).mean()
    # 计算MACD柱
    macd = 2 * (dif - dea)
    return dif, dea, macd



def dingding_push(msg="默认"):
    # 钉钉机器人 Webhook 地址
    url = 'https://oapi.dingtalk.com/robot/send?access_token=d7553b85462ab734bae9096bc384f22975e18ad467f1dbe358c943f6975af542'
    # 要发送的消息内容
    data = {
        "msgtype": "markdown",
        # "text": {
        #     "content": msg
        # },
        "markdown": {
            "title": "行情提示"
                     "",
            "text": msg
        },
        "at": {
            "isAtAll": False
        }
    }
    headers = {'Content-Type': 'application/json;charset=utf-8'}
    # 发送 POST 请求，将消息内容转换为 JSON 格式
    # r = requests.post(url, headers=headers, data=json.dumps(data))
    r = requests.post(url, data=json.dumps(data), headers={'Content-Type': 'application/json'})
    print('钉钉消息发送结果：', r.json())


def initialize_mt5(retries=15, delay=15):
    """
    尝试初始化MT5连接，支持重试。
    :param login: MetaTrader 5的登录账号
    :param password: MetaTrader 5的密码
    :param server: MetaTrader 5服务器的地址
    :param retries: 重试次数
    :param delay: 每次重试之间的延迟时间（秒）
    :return: 如果成功，返回True；如果失败，返回False
    """
    for attempt in range(retries):
        try:
            # todo:根据操作系统选择初始化语句
            if not mt5.initialize(login=5027647314, password="Ui-aGbL3", server="MetaQuotes-Demo", timeout=1000):
                # if not mt5.initialize(login=86552716, password="Mu*vQm4c", server="MetaQuotes-Demo", timeout=1000): # 服务器用
                # if not mt5.initialize():
                print(f"初始化失败: {mt5.last_error()}")
                # dingding_push("提示：MT5初始化失败！")
                mt5.shutdown()
                time.sleep(delay)  # 等待一段时间后重试
            else:
                # print("MT5初始化成功！")
                return True
        except Exception as e:
            print(f"初始化时发生错误: {e}")
            time.sleep(delay)  # 等待一段时间后重试

    print("MT5初始化失败，已达最大重试次数。")
    dingding_push("提示：MT5初始化失败，已达最大重试次数。")
    return False


def get_rates4pos(symbel, level, start=0, num=8000):
    # 根据指定的分钟数，获得对应分、时的数据
    if (level == 5):
        rates = mt5.copy_rates_from_pos(symbel, mt5.TIMEFRAME_M5, start, num)
    if (level == 15):
        rates = mt5.copy_rates_from_pos(symbel, mt5.TIMEFRAME_M15, start, num)
    if (level == 30):
        rates = mt5.copy_rates_from_pos(symbel, mt5.TIMEFRAME_M30, start, num)
    if (level == 60):
        rates = mt5.copy_rates_from_pos(symbel, mt5.TIMEFRAME_H1, start, num)
    if (level == 240):
        rates = mt5.copy_rates_from_pos(symbel, mt5.TIMEFRAME_H4, start, num)
    if (level == 1440):
        rates = mt5.copy_rates_from_pos(symbel, mt5.TIMEFRAME_D1, start, num)
    if (level == 7200):
        rates = mt5.copy_rates_from_pos(symbel, mt5.TIMEFRAME_W1, start, num)
    df = pd.DataFrame(rates)
    return df


def readcsv_rates4range():
    "从csv中读取K线数据，以时间区间方式，用于策略回测"


def readcsv_rates4pos(symbol, start=0, num=500):
    "从csv中读取K线数据，以K线数量方式"


def get_rates4range(symbel, level, utc_from, utc_to):
    # 时间区间方式获得数据，增加：模式：1增量，0初始化
    # 根据指定的分钟数，获得对应分、时的数据
    rates = None
    if level == 5:
        rates = mt5.copy_rates_range(symbel, mt5.TIMEFRAME_M5, utc_from, utc_to)
    elif level == 15:
        rates = mt5.copy_rates_range(symbel, mt5.TIMEFRAME_M15, utc_from, utc_to)
    elif level == 30:
        rates = mt5.copy_rates_range(symbel, mt5.TIMEFRAME_M30, utc_from, utc_to)
    elif level == 60:
        rates = mt5.copy_rates_range(symbel, mt5.TIMEFRAME_H1, utc_from, utc_to)
    elif level == 240:
        rates = mt5.copy_rates_range(symbel, mt5.TIMEFRAME_H4, utc_from, utc_to)
    elif level == 1440:
        rates = mt5.copy_rates_range(symbel, mt5.TIMEFRAME_D1, utc_from, utc_to)
    elif level == 7200:
        rates = mt5.copy_rates_range(symbel, mt5.TIMEFRAME_W1, utc_from, utc_to)
    df = pd.DataFrame(rates)
    return df


def gen_FI(df, alpha=13):
    # 计算强力指标
    tmp_close_ema = pd.DataFrame(ta.EMA(df['close'].values, timeperiod=alpha))
    tmp_open_ema = pd.DataFrame(ta.EMA(df['open'].values, timeperiod=alpha))
    volume = pd.DataFrame(df['tick_volume'].values)
    return (tmp_close_ema - tmp_open_ema.shift(1)) * volume


def gen_ADX(df):
    # 生成ADX和DI数据
    # 计算真实波幅TR
    tmp_df = pd.DataFrame()
    tmp_df['tr'] = 0
    # df['tr'] = 0
    for i in range(1, len(df)):
        tmp_df.loc[i, 'tr'] = max(
            df.loc[i, 'high'] - df.loc[i, 'low'],
            abs(df.loc[i, 'high'] - df.loc[i - 1, 'close']),
            abs(df.loc[i, 'low'] - df.loc[i - 1, 'close'])
        )
    # 计算上升动向 +DM
    tmp_df['+dm'] = 0
    for i in range(1, len(df)):
        if df.loc[i, 'high'] > df.loc[i - 1, 'high'] and df.loc[i, 'low'] >= df.loc[i - 1, 'low']:
            tmp_df.loc[i, '+dm'] = df.loc[i, 'high'] - df.loc[i - 1, 'high']
        else:
            tmp_df.loc[i, '+dm'] = 0

    # 计算下降动向 -DM
    tmp_df['-dm'] = 0
    for i in range(1, len(df)):
        if df.loc[i, 'low'] < df.loc[i - 1, 'low'] and df.loc[i, 'high'] <= df.loc[i - 1, 'high']:
            tmp_df.loc[i, '-dm'] = df.loc[i - 1, 'low'] - df.loc[i, 'low']
        else:
            tmp_df.loc[i, '-dm'] = 0
    # 设定周期
    period = 14

    # 计算周期内的 +DM、-DM、TR 的总和，这里简单用移动窗口求和（实际中可能要更严谨处理前期数据不足情况）
    tmp_df['+dm_sum'] = tmp_df['+dm'].rolling(window=period).sum()
    tmp_df['-dm_sum'] = tmp_df['-dm'].rolling(window=period).sum()
    tmp_df['tr_sum'] = tmp_df['tr'].rolling(window=period).sum()

    # 计算 +DI 和 -DI
    tmp_df['+di'] = 100 * tmp_df['+dm_sum'] / tmp_df['tr_sum']
    tmp_df['-di'] = 100 * tmp_df['-dm_sum'] / tmp_df['tr_sum']
    # 计算趋向值DX
    tmp_df['dx'] = 100 * (tmp_df['+di'] - tmp_df['-di']).abs() / (tmp_df['+di'] + tmp_df['-di'])
    # 计算平均趋向指数ADX，同样设定周期为14（可按需调整）
    tmp_df['adx'] = tmp_df['dx'].rolling(window=period).mean()
    tmp_df['atr'] = tmp_df['tr'].rolling(window=period).mean()
    df['DI+'] = tmp_df['+di']
    df['DI-'] = tmp_df['-di']
    df['ADX'] = tmp_df['adx']
    df['ATR'] = tmp_df['atr']


def gen_ADX_probability(ADX, DI_plus, DI_subtract):
    # 通过ADX指标判断趋势
    pre1_ADX = ADX.iloc[-1]
    pre1_DI_plus = DI_plus.iloc[-1]
    pre1_DI_subtract = DI_subtract.iloc[-1]
    pre2_ADX = ADX.iloc[-2]
    pre2_DI_plus = DI_plus.iloc[-2]
    pre2_DI_subtract = DI_subtract.iloc[-2]
    pre3_ADX = ADX.iloc[-3]
    pre3_DI_plus = DI_plus.iloc[-3]
    pre3_DI_subtract = DI_subtract.iloc[-3]
    pre4_ADX = ADX.iloc[-4]
    pre4_DI_plus = DI_plus.iloc[-4]
    pre4_DI_subtract = DI_subtract.iloc[-4]
    pre5_ADX = ADX.iloc[-5]
    pre5_DI_plus = DI_plus.iloc[-5]
    pre5_DI_subtract = DI_subtract.iloc[-5]
    # indexes1 = (pre1_ADX, pre1_DI_plus, pre1_DI_subtract,
    #             pre2_ADX, pre2_DI_plus, pre2_DI_subtract,
    #             pre3_ADX, pre3_DI_plus, pre3_DI_subtract,
    #             pre4_ADX, pre4_DI_plus, pre4_DI_subtract,
    #             pre5_ADX, pre5_DI_plus, pre5_DI_subtract)
    # indexes1_keep4decimal = tuple(round(x, 2) for x in indexes1)
    # print(indexes1_keep4decimal[0:3])
    # print(indexes1_keep4decimal[3:6])
    # print(indexes1_keep4decimal[6:9])
    # print(indexes1_keep4decimal[9:12])
    # print(indexes1_keep4decimal[12:15])
    probability = 0
    if pre1_DI_plus > pre1_DI_subtract:
        probability = probability + 1
        satus = ('up', probability)
        if pre2_DI_plus > pre2_DI_subtract:
            probability = probability + 1
            satus = ('up-up', probability)
            if pre3_DI_plus > pre3_DI_subtract:
                probability = probability + 1
                satus = ('up-up-up', probability)
        elif pre2_DI_plus <= pre2_DI_subtract:
            satus = ('down-up', probability)
            if pre3_DI_plus > pre3_DI_subtract:
                probability = probability + 1
                satus = ('up-down-up', probability)
            elif pre3_DI_plus <= pre3_DI_subtract:
                satus = ('down-down-up', probability)
    elif pre1_DI_plus < pre1_DI_subtract:
        probability = probability + 1
        satus = ('down', probability)
        if pre2_DI_plus < pre2_DI_subtract:
            probability = probability + 1
            satus = ('down-down', probability)
            if pre3_DI_plus < pre3_DI_subtract:
                probability = probability + 1
                satus = ('down-down-down', probability)
            elif pre3_DI_plus > pre3_DI_subtract:
                satus = ('up-down-down', probability)
        elif pre2_DI_plus > pre2_DI_subtract:
            satus = ('up-down', probability)
            if pre3_DI_plus > pre3_DI_subtract:
                satus = ('up-up-down', probability)
            elif pre3_DI_plus < pre3_DI_subtract:
                satus = ('down-up-down', probability)
    elif pre1_DI_plus == pre1_DI_subtract:
        satus = ('equal', probability)
        if pre2_DI_plus < pre2_DI_subtract:
            satus = ('down-equal', probability)
        elif pre2_DI_plus > pre2_DI_subtract:
            satus = ('up-equal', probability)
    else:
        satus = ('chaos', probability)
    ADX_satus = 'chaos'
    if pre1_ADX > pre2_ADX:
        if pre3_ADX < pre2_ADX:
            ADX_satus = ('up-up')
        else:
            ADX_satus = ('down-up')
    elif pre1_ADX < pre2_ADX:
        if pre3_ADX > pre2_ADX:
            ADX_satus = ('down-down')
        else:
            ADX_satus = ('up-down')
    return satus, ADX_satus


def gen_Kcolor(df):
    # 按照11日均线方向和MACD方向，判断K线应该有的颜色
    # -是在0轴下，+在零轴上
    tmp_df = pd.DataFrame() # 格式化的数据
    for i in range(1, len(df)):
        if df.loc[i-1,'EMA11'] < df.loc[i,'EMA11'] and df.loc[i-1,'Hist'] < df.loc[i,'Hist']:
            if df.loc[i,'Hist'] < 0:  tmp_df.loc[i,'Kcol'] = 'green-'
            elif df.loc[i,'Hist'] >= 0:  tmp_df.loc[i,'Kcol'] = 'green+'
        elif df.loc[i-1,'EMA11'] > df.loc[i,'EMA11'] and df.loc[i-1,'Hist'] > df.loc[i,'Hist']:
            if df.loc[i,'Hist']<0:  tmp_df.loc[i,'Kcol'] = 'red-'
            elif df.loc[i,'Hist']>=0:  tmp_df.loc[i,'Kcol'] = 'red+'
        elif df.loc[i-1,'EMA11'] < df.loc[i,'EMA11'] and df.loc[i-1,'Hist'] > df.loc[i,'Hist']:
            if df.loc[i,'Hist'] < 0:  tmp_df.loc[i,'Kcol'] = 'blue-'
            elif df.loc[i,'Hist'] >=0 :  tmp_df.loc[i,'Kcol'] = 'blue+'
        elif df.loc[i-1,'EMA11'] > df.loc[i,'EMA11'] and df.loc[i-1,'Hist'] < df.loc[i,'Hist']:
            if df.loc[i,'Hist'] < 0 :  tmp_df.loc[i,'Kcol'] = 'blue-'
            elif df.loc[i,'Hist'] >=0 : tmp_df.loc[i,'Kcol'] = 'blue+'
        else: tmp_df.loc[i,'Kcol'] = 'None'
    return tmp_df['Kcol']


# 生成金叉死叉数据和K线颜色：
def gen_cross_kcolor(df):
    tmp_df = pd.DataFrame()
    for i in range(1, len(df)):
        # 判断金叉死叉
        # MACD从下向上穿过信号线 (金叉)
        golden_cross = (df.loc[i, 'MACD'] > df.loc[i, 'Signal']) & (
                    df.loc[i - 1, 'MACD'] <= df.loc[i - 1, 'Signal'])
        # MACD从上向下穿过信号线 (死叉)
        dead_cross = (df.loc[i, 'MACD'] < df.loc[i, 'Signal']) & (
                    df.loc[i - 1, 'MACD'] >= df.loc[i - 1, 'Signal'])
        # print(golden_cross)
        if golden_cross:
            tmp_df.loc[i, 'cross'] = 1
        # print(dead_cross)
        elif dead_cross:
            tmp_df.loc[i, 'cross'] = -1
        else:
            tmp_df.loc[i, 'cross'] = 0
        # 生成K线颜色数据
        if df.loc[i - 1, 'EMA11'] < df.loc[i, 'EMA11'] and df.loc[i - 1, 'Hist'] < df.loc[i, 'Hist']:
            if df.loc[i, 'Hist'] < 0:
                tmp_df.loc[i, 'Kcolor'] = 'green-'
            elif df.loc[i, 'Hist'] >= 0:
                tmp_df.loc[i, 'Kcolor'] = 'green+'
        elif df.loc[i - 1, 'EMA11'] > df.loc[i, 'EMA11'] and df.loc[i - 1, 'Hist'] > df.loc[i, 'Hist']:
            if df.loc[i, 'Hist'] < 0:
                tmp_df.loc[i, 'Kcolor'] = 'red-'
            elif df.loc[i, 'Hist'] >= 0:
                tmp_df.loc[i, 'Kcolor'] = 'red+'
        elif df.loc[i - 1, 'EMA11'] < df.loc[i, 'EMA11'] and df.loc[i - 1, 'Hist'] > df.loc[i, 'Hist']:
            if df.loc[i, 'Hist'] < 0:
                tmp_df.loc[i, 'Kcolor'] = 'blue-'
            elif df.loc[i, 'Hist'] >= 0:
                tmp_df.loc[i, 'Kcolor'] = 'blue+'
        elif df.loc[i - 1, 'EMA11'] > df.loc[i, 'EMA11'] and df.loc[i - 1, 'Hist'] < df.loc[i, 'Hist']:
            if df.loc[i, 'Hist'] < 0:
                tmp_df.loc[i, 'Kcolor'] = 'blue-'
            elif df.loc[i, 'Hist'] >= 0:
                tmp_df.loc[i, 'Kcolor'] = 'blue+'
        else:
            tmp_df.loc[i, 'Kcolor'] = 'None'
    return tmp_df

# 判断价格是远离、接近还是超越布林带
def judge_relationship(row):
    # 判断上轨
    if row['close'] > row['BBANDS_Up']:
        upper_status = '超越'
    elif row['Distance_to_Upper_Band'] > 0:
        upper_status = '远离'
    else:
        upper_status = '接近'

    # 判断下轨
    if row['close'] < row['BBANDS_Low']:
        lower_status = '超越'
    elif row['Distance_to_Lower_Band'] > 0:
        lower_status = '远离'
    else:
        lower_status = '接近'
    return upper_status, lower_status

def generate_data(df):
    # 根据获得的K线数据，计算各种指标数值,并保存
    try:
        # pd.options.mode.copy_on_write = False
        df['TradeTime'] = pd.to_datetime(df['time'], unit='s').dt.strftime('%Y.%m.%d %H:%M:%S')
        df['body_length'] = abs(df['close'] - df['open']) # 计算每根K线的实体长度（绝对值）
        df['total_length'] = df['high'] - df['low']
        df['EMA11'] = ta.EMA(df['close'].values, timeperiod=11)
        df['EMA13'] = ta.EMA(df['close'].values, timeperiod=13)
        df['EMA22'] = ta.EMA(df['close'].values, timeperiod=22)
        df['EMA26'] = ta.EMA(df['close'].values, timeperiod=26)

        upper, middle, lower = ta.BBANDS(df['close'], timeperiod=22, nbdevup=2, nbdevdn=2, matype=0)
        upper2, middle2, lower2 = ta.BBANDS(df['close'], timeperiod=22, nbdevup=4, nbdevdn=4, matype=0)
        df['BBANDS_Up'] = upper
        df['BBANDS_Mid'] = middle
        df['BBANDS_Low'] = lower
        df['BBANDS_Up2'] = upper2
        df['BBANDS_Low2'] = lower2

        # 计算价格与布林上下轨的距离
        df['distance_up2'] = ((df['close'] - df['BBANDS_Up2']) / df['BBANDS_Up2']) * 100
        df['distance_low2'] = ((df['close'] - df['BBANDS_Low2']) / df['BBANDS_Low2']) * 100
        # df['Distance_to_Upper_Band'] = df['BBANDS_Up'] - df['close']  # 价格与上轨的距离
        # df['Distance_to_Lower_Band'] = df['close'] - df['BBANDS_Low']  # 价格与下轨的距离
        # df['Distance_to_Upper_Band2'] = df['BBANDS_Up2'] - df['close']  # 价格与4倍上轨的距离
        # df['Distance_to_Lower_Band2'] = df['close'] - df['BBANDS_Low2']  # 价格与4倍下轨的距离
        # df['D2UB2_change'] = df['Distance_to_Upper_Band2'].diff()
        # df['D2UB2_trend'] = np.where(df['D2UB2_change'] > 0, 'away', 'close')
        # df['D2LB2_change'] = df['Distance_to_Lower_Band2'].diff()
        # df['D2LB2_trend'] = np.where(df['D2LB2_change'] > 0, 'away', 'close')

        # 应用判断函数
        # df[['Upper_Band_Status', 'Lower_Band_Status']] = df.apply(judge_relationship, axis=1, result_type='expand')

        # MACD数据生成
        macd, macdsignal, macdhist = ta.MACD(df['close'].values, fastperiod=12, slowperiod=26, signalperiod=9)
        df['MACD'] = macd
        df['Signal'] = macdsignal
        df['Hist'] = macdhist * 2
        cross_kcolor = gen_cross_kcolor(df)
        df['cross'] = cross_kcolor['cross']
        df['Kcolor'] = cross_kcolor['Kcolor']
        # df['Underwater_Golden_Cross'] = (df['MACD'] < 0) & (df['Signal'] < 0) & (
        #         df['MACD'].shift(1) < df['Signal'].shift(1)) & (df['MACD'] > df['Signal'])
        # df['Above_Death_Cross'] = (df['MACD'] > 0) & (df['Signal'] > 0) & (
        #         df['MACD'].shift(1) > df['Signal'].shift(1)) & (df['MACD'] < df['Signal'])
        gen_ADX(df)
        # 计算EMA的变化量（斜率的一种近似）
        # df['EMA_Change'] = df['EMA22'].diff()
        # 判断趋势（基于EMA变化量的符号）
        # df['trend'].iloc[0] = 'Unknown'  # 或者你可以选择删除这一行，因为它没有足够的数据来判断趋势
        # df['trend'] = np.where(df['EMA_Change'] > 0, 'Up', 'Down')
        # 注意：这里我们简单地根据EMA变化量的正负来判断趋势，但实际应用中可能需要更复杂的逻辑来处理边缘情况（如EMA变化量接近0时）
        # 由于第一个EMA变化量是NaN（因为没有前一个EMA值来比较），我们将其趋势设置为未知或忽略
        # df['FI'] = gen_FI(df)  # 生成强力指数
        # df['Kgreen'] = (df['EMA11'].shift(1) < df['EMA11'] )& (df['Hist'].shift(1) < df['Hist'])
        # df['Kcolor'].iloc[0] = 'Unknown'
    except Exception as e:
        print("报错：", e)
    # todo:增加假突破、关键位
    return df


def write_2csv(df, symbol, level):
    title = convert_title(level)
    csv_path = f'data/{symbol}/{symbol}_{title}.csv'
    try:
        df.columns = df.columns.str.replace('=', '')
        df.to_csv(csv_path, index=False)
        # 如果没有异常，文件保存成功
        print(f"目标：{symbol}，级别：{title}，数据已经保存到：{csv_path}！")
    except Exception as e:
        print(f"保存CSV文件时发生错误: {e}")


def read4csv(symbel, level):
    title = convert_title(level)
    try:
        res = pd.read_csv(f'data/{symbel}/{symbel}_{title}.csv')
    except Exception as e:
        print(f"读取CSV文件时发生错误: {e}")
    return res


def convert_title(level):
    # 根据分钟转为分钟或小时
    title = ''
    for i in range(1):
        if (level == 5):
            title = 'M5'
        elif (level == 15):
            title = 'M15'
        elif (level == 30):
            title = 'M30'
        elif (level == 60):
            title = 'H1'
        elif (level == 240):
            title = 'H4'
        elif (level == 1440):
            title = 'D1'
        elif (level == 7200):
            title = 'W1'
    if title == '': raise ValueError("交易周期不能为空！")
    return title

def conversion_time4findH1(test_time):
    # 将'datetime'列转换为datetime类型,将分钟、秒改为00,转为整点
    test_time_dt = pd.to_datetime(test_time)
    trade_time = test_time_dt.replace(minute=0, second=0)
    return trade_time


def H4fH1(test_time):
    # 交易时间转为H4级别K线时间
    global trade_time
    test_time_dt = pd.to_datetime(test_time)
    mh = test_time_dt.hour
    if mh < 4:
        trade_time = test_time_dt.replace(hour=0, minute=0, second=0)
    elif mh >= 4 and mh < 8:
        trade_time = test_time_dt.replace(hour=4, minute=0, second=0)
    elif mh >= 8 and mh < 12:
        trade_time = test_time_dt.replace(hour=8, minute=0, second=0)
    elif mh >= 12 and mh < 16:
        trade_time = test_time_dt.replace(hour=12, minute=0, second=0)
    elif mh >= 16 and mh < 20:
        trade_time = test_time_dt.replace(hour=16, minute=0, second=0)
    elif mh >= 20 and mh <= 23:
        trade_time = test_time_dt.replace(hour=20, minute=0, second=0)
    return trade_time


def M15ftradetime(test_time):
    # 交易时间转为M15级别K线时间
    global min_time
    test_time_dt = pd.to_datetime(test_time)
    minute = test_time_dt.minute
    if minute <= 15:
        min_time = test_time_dt.replace(minute=0, second=0)
    elif minute > 15 and minute <= 30:
        min_time = test_time_dt.replace(minute=15, second=0)
    elif minute > 30 and minute <= 45:
        min_time = test_time_dt.replace(minute=30, second=0)
    elif minute > 45:
        min_time = test_time_dt.replace(minute=45, second=0)
    return min_time


def conversion_tradetime(test_time, level):
    # 将输入的交易时间按照交易级别level，转为整点的时间戳
    # 分割字符串
    if not isinstance(test_time, str):
        test_time_str = test_time.strftime('%Y.%m.%d %H:%M:%S')
    parts = test_time_str.split()
    time_parts = parts[-1].split(':')  # 假设时间总是在字符串的最后一部分，并且由冒号分隔
    hour = int(time_parts[0])
    minute = int(time_parts[1])
    if level == 5:
        if minute < 55:
            remainder = minute % 5
            minute = minute - remainder
        if minute >= 55:
            minute = 55
        time_parts[1] = str(minute)
    if level == 15:
        if minute < 45:
            remainder = minute % 15
            minute = minute - remainder
        if minute >= 45:
            minute = 45
        time_parts[1] = str(minute)
    if level == 60:
        # 替换分钟和秒
        time_parts[1] = '00'  # 分钟
    if level == 240:
        remainder = hour % 4
        if hour > 4 and remainder != 0:
            hour = hour - remainder
        elif hour > 0 and hour <= 4:
            hour = 4
        else:
            hour = 0
        time_parts[0] = str(hour)
        time_parts[1] = '00'
    if level == 1440:
        time_parts[0] = '00'  # 小时
        time_parts[1] = '00'  # 分钟
    if level == 7200:
        date = parts[:-1][0]
        date_obj = datetime.strptime(date, "%Y.%m.%d")
        weekday = date_obj.weekday()
        if weekday != 0:
            # date_obj = date_obj - timedelta(days=weekday)  # 以星期一作为第一天
            date_obj = get_previous_sunday(date_obj)  # 以星期一作为第一天
        # input(2222)
        parts[:-1] = [date_obj.strftime('%Y.%m.%d')]
        time_parts[0] = '00'  # 小时
        time_parts[1] = '00'  # 分钟
    time_parts[2] = '00'  # 秒
    # 使用strptime函数解析字符串
    new_time_str = ':'.join(time_parts)
    new_data_time = ' '.join(parts[:-1] + [new_time_str])
    # date_time_obj = datetime.strptime(new_data_time, '%Y.%m.%d %H:%M:%S') #1726815600 1726844400
    # date_time_obj = datetime.strptime(new_data_time, '%Y-%m-%d %H:%M:%S')  # .replace(tzinfo=timezone.utc)  #1726815600 1726844400
    # 将datetime对象转换为时间戳
    # timestamp = int(date_time_obj.timestamp()) #+ 28800 增加秒，不准确，replace(tzinfo=timezone.utc) 设定UTC
    return new_data_time


def detect_kangaroo_tail(df, window=200):
    """
    检测袋鼠尾形态
    """
    # 计算平均实体长度
    df['avg_body_length'] = df['body_length'].rolling(window).mean()

    # 计算条件
    condition1 = df['total_length'] > 3 * df['avg_body_length']  # 三倍于平均长度
    condition2 = (df['body_length'] / df['total_length']) < 0.2  # 实体占比小于20%

    # 标记袋鼠尾
    df['kangaroo_tail'] = np.where(condition1 & condition2, 1, 0)
    return df


def get_previous_sunday(input_date):
    """
    返回输入日期所在周的星期日（本周的第一天）
    :param input_date: datetime 对象
    :return: 本周的星期日日期
    """
    # 计算当前日期是星期几（0=星期一，6=星期日）
    weekday = input_date.weekday()  # weekday() 返回 0（星期一）到 6（星期日）
    # 计算距离上一个星期日有多少天
    days_to_sunday = (weekday + 1) % 7  # 转换为距离上周日的天数
    # 减去天数，得到上一个星期日的日期
    previous_sunday = input_date - timedelta(days=days_to_sunday)
    return previous_sunday


def judge_trend(df, what_ema):
    # 识别均线的反转和趋势，用三点判断-3,-2，-1；上升、下降、反转向上、反转向下，
    # down reverse_up up reverse_down
    pre_pre_value = df[what_ema].iloc[-3]  # round(df[what_ema].iloc[-3], 5)
    pre_value = df[what_ema].iloc[-2]  # round(df[what_ema].values[-2], 5)
    cur_value = df[what_ema].iloc[-1]  # round(df[what_ema].values[-1], 5)
    if (pre_pre_value > pre_value) and (pre_value > cur_value):
        # print("下降趋势")
        return 'down', "下降趋势"
    if (pre_pre_value > pre_value) and (pre_value < cur_value):
        # print("下跌后向上反转")
        return 'reverse_up', "反转向上"
    if (pre_pre_value < pre_value) and (pre_value < cur_value):
        # print("上升趋势")
        return 'up', "上升趋势"
    if (pre_pre_value < pre_value) and (pre_value > cur_value):
        # print("上升后向下反转")
        return 'reverse_down', "反转向下"
    else:
        return 'chaos', "混沌状态"


def judge_macd_cross(df):
    pre_pre_macd = df['Hist'].iloc[-3]
    pre_macd = df['Hist'].iloc[-2]
    cur_macd = df['Hist'].iloc[-1]
    # print(pre_pre_macd,pre_macd,cur_macd)
    axle = ''
    macd_color = ''
    if cur_macd < 0:
        axle = 'under'  # 水下
    if cur_macd > 0:
        axle = 'above'  # 水上
        # 判断水下金叉和水上死叉
    df['Underwater_Golden_Cross'] = (df['MACD'] < 0) & (df['Signal'] < 0) & (
            df['MACD'].shift(1) < df['Signal'].shift(1)) & (df['MACD'] > df['Signal'])
    df['Above_Death_Cross'] = (df['MACD'] > 0) & (df['Signal'] > 0) & (
            df['MACD'].shift(1) > df['Signal'].shift(1)) & (df['MACD'] < df['Signal'])

def judge_Kline_color(df, what_ema):
    # down reverse_up up reverse_down 'axle_under''axle_above'
    # 牛绿禁止卖空，熊红禁止买入。要买入需周线不为红色，卖出不为绿，蓝色任方向。
    # 大中级别绿色可买入，其中一个变红或变蓝，兑现收益；上涨时两个级别都不红，卖空两个都不绿。买入两个都不红。
    # 颜色不再支持交易方向，则马上了结
    direction = 'chaos'
    K_color = ''
    axle = ''
    Mhist = 'None'
    pre_pre_macd = df['Hist'].iloc[-3]
    pre_macd = df['Hist'].iloc[-2]
    cur_macd = df['Hist'].iloc[-1]
    pre_pre_value = df[what_ema].iloc[-3]  # round(df[what_ema].iloc[-3], 5)
    pre_value = df[what_ema].iloc[-2]  # round(df[what_ema].values[-2], 5)
    cur_value = df[what_ema].iloc[-1]  # round(df[what_ema].values[-1], 5)

    if pre_macd < 0:
        if pre_pre_macd > pre_macd :pre_Mhist='red'
        elif pre_pre_macd < pre_macd :pre_Mhist='purple'
        if pre_macd > cur_macd : cur_Mhist='red'
        elif pre_macd < cur_macd : cur_Mhist = 'purple'

    if pre_macd > 0:
        if pre_pre_macd < pre_macd:pre_Mhist='green'
        elif pre_pre_macd > pre_macd:pre_Mhist='blue'
        if pre_macd < cur_macd :cur_Mhist = 'green'
        elif pre_macd > cur_macd:cur_Mhist = 'blue'

    if pre_value > cur_value :
        if cur_Mhist == 'red' : K_color = 'red-'
        elif cur_Mhist == 'blue' : K_color = 'red+'
        else: K_color = 'blue'
    elif pre_value <= cur_value :
        if cur_Mhist == 'purple' : K_color = 'green-'
        elif cur_Mhist == 'green' : K_color = 'green+'
        else: K_color = 'blue'

    return K_color


def judge_macd(df):
    axle = ''
    macd_color = ''
    pre_pre_macd = df['Hist'].iloc[-3]
    pre_macd = df['Hist'].iloc[-2]
    cur_macd = df['Hist'].iloc[-1]
    if cur_macd < 0:
        axle = 'under'  # 水下
    if cur_macd > 0:
        axle = 'above'  # 水上

    if (pre_pre_macd > pre_macd) and (pre_macd > cur_macd):
        # print("MACD下降趋势")
        if axle == 'under': macd_color = 'r'
        if axle == 'above': macd_color = 'gb'
        return 'down', "MACD下降趋势", axle, macd_color
    elif (pre_pre_macd > pre_macd) and (pre_macd < cur_macd):
        # print("MACD下跌后向上反转")
        if axle == 'under': macd_color = 'nrb'
        if axle == 'above': macd_color = 'g'
        return 'reverse_up', "MACD反转向上", axle, macd_color
    elif (pre_pre_macd < pre_macd) and (pre_macd < cur_macd):
        # print("MACD上升趋势")
        if axle == 'under': macd_color = 'rb'
        if axle == 'above': macd_color = 'g'
        return 'up', "MACD上升趋势", axle, macd_color
    elif (pre_pre_macd < pre_macd) and (pre_macd > cur_macd):
        # print("MACD上升后向下反转")
        if axle == 'under': macd_color = 'r'  # 水下，转红
        if axle == 'above': macd_color = 'ngb'  # 水上
        return 'reverse_down', "MACD反转向下", axle, macd_color
    else:
        return 'chaos', '混沌状态', axle


def is_value_range(df):
    # 与价值区间的位置关系
    close = df['close'].values[-1]
    ema13 = df['EMA13'].values[-1]
    ema26 = df['EMA26'].values[-1]
    if (close > ema13) & (close > ema26):
        return 'above'
    elif (close < ema13) & (close < ema26):
        return 'under'
    elif ((close <= ema13) & (close >= ema26)) | ((close > ema13) & (close < ema26)):
        return 'in'
    else:
        return 'chaos'

def dynamical_system(trend, macd):
    # 判断K线应该是红、绿、蓝，
    trend = trend[0]
    macd_status = macd[0]
    # print(trend,macd_status)
    if 'down' == trend and 'down' == macd_status:
        # print("green")  # 禁止卖空，买入或观望
        return 'red'
    elif 'up' == trend and 'up' == macd_status:
        # print("red")  # 禁止买入，卖出或观望
        return 'green'
    elif 'reverse_down' == trend and 'reverse_down' == macd_status:
        # print("new_green_blue")  # 偏向卖出
        return 'closely_green_behind'
    elif 'reverse_up' in trend and 'reverse_up' == macd_status:
        # print("new_red_blue")  # 偏向买入
        return 'closely_red_behind'
    else:
        # print('blue')
        return 'blue'

def save2SQLite(data):
    pass

conn = sqlite3.connect('example.db')
c = conn.cursor()
# 创建表
c.execute('''CREATE TABLE IF NOT EXISTS stocks
            (date text, trans text, symbol text, qty real, price real)''')
# 插入数据
c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
conn.commit()
conn.close()


def save2mysql(symbol, level, data):
    # 数据库连接信息
    username = 'jiao'
    password = 'gQ123456'
    host = '47.94.195.132'
    database = 'tradedb'

    # 使用SQLAlchemy创建数据库引擎
    engine = create_engine(f'mysql+pymysql://{username}:{password}@{host}/{database}')
    # 存储数据到mysql
    data.to_sql(name='XAUUSD_M15.test', con=engine, if_exists='append',
                index=False)  # if_exists='replace'append
    # # 指定读取列
    # col_df = pd.read_sql_table('XAUUSD_M15.test',engine,columns=['open','close'])
    # print(col_df)
    # 指定查询条件，查询数据
    query = '''
    SELECT * FROM `XAUUSD_M15.test` where open > 2700  and tick_volume > 1500 limit 17
    '''
    query_df = pd.read_sql_query(query, engine)
    print(query_df)


def sava_img(df, symbol, title):
    data = df[['TradeTime', 'open', 'high', 'low', 'close']][-50:]
    data.index = pd.to_datetime(data['TradeTime'].astype('str'))
    data.drop(columns=['TradeTime'], inplace=True)
    # 指定列
    data.columns = ['open', 'high', 'low', 'close']
    data.sort_index(inplace=True)
    # #可视化,指标保存为图片
    now_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
    ema_13 = df['EMA13'][-50:]
    ema_26 = df['EMA26'][-50:]
    add_plot = [
        # 画EMA13
        mpf.make_addplot(ema_13, color='r'),
        # 画EMA26
        mpf.make_addplot(ema_26, color='y')
    ]
    # 指定图形属性
    my_color = mpf.make_marketcolors(up='red', down='cyan', volume='blue', inherit=True)
    my_style = mpf.make_mpf_style(
        facecolor='black',
        marketcolors=my_color,
        gridaxis='horizontal',
        gridcolor='red',
        gridstyle='--',
        y_on_right=False
    )
    mpf.plot(
        data,
        addplot=add_plot,
        type='candle',
        style=my_style,
        figratio=(5, 3),
        figscale=2,  # volume=True
        savefig=f'./img/{symbol}_{title}_{now_time}.png'
    )


def price_position_percentage(price, upper_band, lower_band):
    """
    计算价格在布林通道的位置百分比

    :param price: 当前价格
    :param rolling_mean: 布林通道中轨线
    :param upper_band: 布林通道上轨线
    :param lower_band: 布林通道下轨线
    :return: 价格在布林通道的位置百分比
    """
    if price >= upper_band:
        return 100
    elif price <= lower_band:
        return 0
    else:
        channel_width = upper_band - lower_band
        distance_from_lower = price - lower_band
        percentage = (distance_from_lower / channel_width) * 100
        return percentage


def price_position_2percentage(price, upper_band, lower_band):
    """
    计算价格在布林通道的位置百分比

    :param price: 当前价格
    :param rolling_mean: 布林通道中轨线
    :param upper_band: 布林通道上轨线
    :param lower_band: 布林通道下轨线
    :return: 价格在布林通道的位置百分比
    """
    if price >= upper_band:
        return 100
    elif price <= lower_band:
        return 0
    else:
        channel_width = (upper_band - lower_band)
        distance_from_lower = price - lower_band
        percentage = (distance_from_lower / channel_width) * 100
        return percentage


def check_position(x, a, b):
    # below下，above上
    # 如果 a 不小于 b，则交换它们以确保 a < b
    if a >= b:
        a, b = b, a
    if x < a:
        return "below"
    elif a <= x <= b:
        return "between"
    else:
        return "above"


def MACD_cross(df):
    """
    判断MACD柱是水下金叉还是水上死叉，主要用于小级别判断
    :param df: dataframe格式数据
    :return: 水下金叉，返回gold；水上死叉，返回crash
    """
    # 计算MACD指标
    df['MACD'], df['Signal'], df['Histogram'] = talib.MACD(df['close'], fastperiod=12, slowperiod=26, signalperiod=9)
    # 判断水下金叉和水上死叉
    df['Signal_Underwater_Golden_Cross'] = (df['MACD'] < 0) & (df['Signal'] < 0) & (
            df['MACD'].shift(1) < df['Signal'].shift(1)) & (df['MACD'] > df['Signal'])
    df['Signal_Above_Death_Cross'] = (df['MACD'] > 0) & (df['Signal'] > 0) & (
            df['MACD'].shift(1) > df['Signal'].shift(1)) & (df['MACD'] < df['Signal'])


def judge_price4value(price,ema11,ema22):
    if ema11 > ema22 :
        on4value = ema11
        under4value = ema22
    else:
        under4value = ema11
        on4value = ema22
    if price < under4value : return 'below_value_zone'
    elif price > on4value : return 'above_value_zone'
    elif price > under4value and price < on4value : return 'in_value_zone'
    else : return 'chaos'


if __name__ == '__main__':
    if mt5.terminal_info() == None:
        initialize_mt5()
        print("执行MT5初始化！")
    # 以下代码用于测试
    symbol = 'XAUUSD'
    min_level = 15
    mid_level = 60
    large_level = 240
    i=0
    k_number = 1000
    rates_df = get_rates4pos(symbol, mid_level, 0, k_number)
    tem_rates_df = rates_df[['time', 'open', 'high', 'low', 'close', 'tick_volume']]
    gen_df = generate_data(tem_rates_df)
    write_2csv(gen_df, symbol, mid_level)
    mid_df = read4csv(symbol, mid_level)
    df1 = detect_kangaroo_tail(mid_df)
    n = len(df1)
    # print(n)
    # print(df1.loc[3]['kangaroo_tail'])
    # print(df1.loc[3])
    # input('=====分割 01 暂停=====')

    for j in range(n):
        if df1.loc[j]['kangaroo_tail'] == 1:
            print(df1.loc[j])
            input('=====分割 01 暂停=====')

    print(df1.tail(100))
    input('=====分割 01 暂停=====')

    with open('config.yaml', 'r', encoding='utf-8') as f:
        config = yaml.safe_load(f)
        run_mod = config['run_mod']
        begin = config['begin']
        # symbol = config['symbols'][0]
        # min_level = config['level']['min_level']
        # mid_level = config['level']['mid_level']
        # large_level = config['level']['lar_level']
        # level_str = config['level']['title']

    begin_time = datetime.strptime(begin, "%Y.%m.%d %H:%M:%S")
    lar_df = read4csv(symbol, large_level)
    # mid_df = read4csv(symbol, mid_level)
    # min_df = read4csv(symbol, min_level)
    while True:
        test_time = begin_time + pd.Timedelta(minutes=mid_level * i)
        i += 1
        if test_time.weekday() >= 5:
            # 跳过周末
            continue
        min_test_time = conversion_tradetime(test_time, min_level)
        mid_test_time = conversion_tradetime(test_time, mid_level)
        lar_test_time = conversion_tradetime(test_time, large_level)

        # min_df['TradeTime'] = pd.to_datetime(min_df['TradeTime'])
        # mid_df['TradeTime'] = pd.to_datetime(mid_df['TradeTime'])
        lar_df['TradeTime'] = pd.to_datetime(lar_df['TradeTime'])

        # min_target_df = min_df[min_df['TradeTime'] <= min_test_time]
        # mid_target_df = mid_df[mid_df['TradeTime'] <= mid_test_time]
        lar_target_df = lar_df[lar_df['TradeTime'] <= lar_test_time]

        # price = mid_target_df['close'].values[-1]
        # now = str(test_time)

        # 非超买情况下
        # 计算价格与上轨的距离（百分比）
        distance = ((lar_target_df['close'] - lar_target_df['BBANDS_Up']) / lar_target_df['BBANDS_Up']) * 100


        # 检查最近lookback_period期的距离变化趋势
        recent_distances = distance.dropna().tail(3)
        # 判断距离是否越来越大（越来越负表示价格低于上轨且距离扩大）
        if len(recent_distances) >= 2:
            # 计算距离变化率
            changes = np.diff(recent_distances)

            # 如果所有变化都为负（距离绝对值越来越大）
            is_increasing_distance = all(c < 0 for c in changes)

            # 或者使用线性回归判断趋势
            # x = np.arange(len(recent_distances))
            # slope = np.polyfit(x, recent_distances, 1)[0]
            # is_increasing_distance = slope < 0
        print(distance.tail(3))
        print(is_increasing_distance)

        input("===========分割线=========")

'''
    if mt5.terminal_info() == None:
        initialize_mt5()
        print("执行MT5初始化！")

    # 生成数据
    for level in (min_level, mid_level, large_level):  # 15,60,240,1440,7200
        rates_df = get_rates4pos(symbol, level, 0, 10000)
        tem_rates_df = rates_df[['time', 'open', 'high', 'low', 'close', 'tick_volume']]
        gen_df = generate_data(tem_rates_df)
        # print(gen_df.tail(1))

        # EMA评分
        # M柱评分
        # 与布林通道距离
        # FI


        last_time = tem_rates_df['time'][-1:].values
        is_in_cvs = tem_cvs_df['time'].isin(last_time).any()
        if not is_in_cvs:
            # 找出在CSV文件中不存在的数据
            # 使用merge来找到左连接（csv_df）中不存在的数据
            missing_data = pd.merge(tem_rates_df, tem_cvs_df,
                                    on=['time', 'open', 'high', 'low', 'close', 'tick_volume'],
                                    how='left', indicator=True)
            missing_data = missing_data[missing_data['_merge'] == 'left_only']
            missing_data = missing_data.drop(columns=['_merge'])
            # 将不存在的数据添加到CSV文件中
            # 先合并数据，再保存回CSV
            tem_cvs_df = pd.concat([tem_cvs_df, missing_data], ignore_index=False)
            tem_cvs_df['tick_volume'] = tem_cvs_df['tick_volume'].astype('int32')
            tem_cvs_df = generate_data(tem_cvs_df[['time', 'open', 'high', 'low', 'close', 'tick_volume']])
            tem_cvs_df.to_csv(path, index=False)
            print("数据更新完成，并保存到", path)

            # tem_4csv = tem_cvs_df.set_index('TradeTime')
            # res = tem_4csv.loc['2024.10.18 23:30:00']
            # res = tem_4csv.loc[999]
            # save2mysql(symbol,level,tem_cvs_df) 

        else:
            print("已经是最新数据！")
       

        # for index, row in rates_df.iterrows():
        #     print(f"Index: {index}")
        #     print(f"Row data: {row['time']},  {row['open']},  ,{row['high']}")
        #     new_row = row['time'], row['open'], row['high'], row['close'], row['tick_volume']
        #     print(new_row)
        #     input(333)
        # print()

    xau_pd = read4csv(symbol, mid_level)
    # print(xau_pd['close'][-3:])
    # 最高价、最低价和收盘价的数据，分别存储在high、low和close变量中
    # 这些变量应该是Pandas的Series对象，索引是日期
    adx = ta.ADX(xau_pd['high'], xau_pd['low'], xau_pd['close'], timeperiod=14)

    # 计算+DI和-DI（在talib中称为PLUS_DI和MINUS_DI）
    plus_di = ta.PLUS_DI(xau_pd['high'], xau_pd['low'], xau_pd['close'], timeperiod=14)
    minus_di = ta.MINUS_DI(xau_pd['high'], xau_pd['low'], xau_pd['close'], timeperiod=14)

    # 识别交叉点
    cross_up = (plus_di > minus_di) & (plus_di.shift(1) <= minus_di.shift(1))  # 上升交叉
    cross_down = (minus_di > plus_di) & (minus_di.shift(1) <= plus_di.shift(1))  # 下降交叉


    # 生成信号（这里只是简单地标记交叉点，实际应用中可能需要更复杂的信号生成逻辑）
    signals = pd.DataFrame(index=xau_pd.index)
    signals['buy_signal'] = np.where(cross_up, 1, 0)  # 1表示买入信号，0表示无信号
    signals['sell_signal'] = np.where(cross_down, -1, 0)  # -1表示卖出信号，0表示无信号

    # 查看结果
    print(signals)

    # 可视化（可选）
    import matplotlib.pyplot as plt

    plt.figure(figsize=(12, 6))
    plt.plot(xau_pd.index, xau_pd['close'], label='Close Price')
    plt.plot(signals[signals['buy_signal'] == 1].index, xau_pd['close'].loc[signals[signals['buy_signal'] == 1].index],
             '^', markersize=10, color='g', label='Buy Signal')
    plt.plot(signals[signals['sell_signal'] == -1].index,
             xau_pd['close'].loc[signals[signals['sell_signal'] == -1].index], 'v', markersize=10, color='r',
             label='Sell Signal')
    plt.legend()
    plt.show()

    # 判断当前价格在价值区间的位置

    # input(44444444444444)

    # 通过喂数据来输出，回测完善，兼容外汇、股票、基金、等数据，组合形成交易策略。评分评级
    # 在talib（Technical Analysis Library）中，直接计算“强度指数”可能不是一个直接的函数，
    # 因为talib库主要聚焦于经典的技术分析指标，如移动平均线、MACD、RSI、Stochastic Oscillator等，以及像ADX（平均方向移动指数）这样的趋势强度指标。

    # 袋鼠尾形成判断，之前和当前是否是

    # 与关键位的距离
'''